package com.alibaba.nacos.common.remote.client.grpc;


import com.alibaba.nacos.api.grpc.auto.Metadata;
import com.alibaba.nacos.api.grpc.auto.Payload;
import com.alibaba.nacos.api.remote.request.Request;
import com.alibaba.nacos.api.remote.response.Response;
import com.alibaba.nacos.common.remote.PayloadRegistry;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream;
import com.google.protobuf.Any;
import com.google.protobuf.UnsafeByteOperations;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class GrpcUtils {

    @SneakyThrows
    public static Payload convert(Request request) {
        Metadata metadata = Metadata.newBuilder().setType(request.getClass().getSimpleName()).setClientIp(InetAddress.getLocalHost().getHostAddress()).putAllHeaders(request.getHeaders()).build();
        byte[] jsonBytes = convertRequest2Byte(request);
        Any.Builder builder = Any.newBuilder().setValue(UnsafeByteOperations.unsafeWrap(jsonBytes));
        return Payload.newBuilder().setMetadata(metadata).setBody(builder).build();
    }

    public static Payload convert(Response response) {
        byte[] jsonBytes = JacksonUtils.toJsonBytes(response);
        Metadata metadata = Metadata.newBuilder().setType(response.getClass().getSimpleName()).build();
        return Payload.newBuilder().setBody(Any.newBuilder().setValue(UnsafeByteOperations.unsafeWrap(jsonBytes)).build()).setMetadata(metadata).build();
    }

    public static <T> T parse(Payload payload) {
        try {
            Class<?> classByType = PayloadRegistry.getClassByType(payload.getMetadata().getType());
            if (classByType == null) {
                log.error("parse payload error, target type not register! payload:{}", payload);
                return null;
            }

            ByteBuffer byteBuffer = payload.getBody().getValue().asReadOnlyByteBuffer();
            Object obj = JacksonUtils.toObj(new ByteBufferBackedInputStream(byteBuffer), classByType);
            if (obj instanceof Request request) {
                request.putAllHeaders(payload.getMetadata().getHeadersMap());
            }
            return (T) obj;
        } catch (Exception e) {
            log.error("parse payload error! payload:{}", payload, e);
            return null;
        }
    }

    private static byte[] convertRequest2Byte(Request request) {
        Map<String, String> headers = new HashMap<>(request.getHeaders());
        request.clearHeaders();
        byte[] jsonBytes = JacksonUtils.toJsonBytes(request);
        request.putAllHeaders(headers);
        return jsonBytes;
    }
}
